package cn.wu.reggie.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.wu.reggie.common.Page;
import cn.wu.reggie.common.R;
import cn.wu.reggie.dto.DishDto;
import cn.wu.reggie.entity.*;
import cn.wu.reggie.exception.BusinessException;
import cn.wu.reggie.mapper.*;
import cn.wu.reggie.service.DishService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.wu.reggie.common.ConstantUtil.DISH_ONSALE;

@Service
@Slf4j
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;

    @Autowired
    private DishFlavorMapper dishFlavorMapper;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private SetmealDishMapper setmealDishMapper;

    @Autowired
    private SetmealMapper setmealMapper;

    @Autowired
    private RedisTemplate redisTemplate;


    @Override
    public R findByPage(Integer pageNum, Integer pageSize,String name) {
        // 1 查询菜品分类列表 把list转换成map （分类id 分类名称）
        List<Category> categoryList = categoryMapper.findAllByType(1);
        Map<Long, String> categoryMap = new HashMap<>();
        categoryList.forEach(c->{
            categoryMap.put(c.getId(),c.getName());
        });

        // 2 设置分页参数
        PageHelper.startPage(pageNum,pageSize);
        // 3根据名称查询所有
        List<Dish> dishList =dishMapper.findByPage(name);

        // 4 将查询的集合封装成 PageInfo
        PageInfo<Dish> pageInfo = new PageInfo<>(dishList);

        // 5 新建一个DtoList 遍历查询的集合将dish封装成dishDto 且给dto设置分类名称 将dto放入dtoList中 设置分类名称
        List<DishDto> dtoList = new ArrayList<>();
        if (CollUtil.isNotEmpty(dishList)) {
            dishList.forEach(dish -> {
//                将dish封装成dishDto
                DishDto dto = new DishDto();
//                =============以往操作==============
//                dto.setId(dish.getId());
//                dto.setName(dish.getName());
//                =============================
//                其实很多工具类bean属性拷贝的操作 只要属性名能对应上就会复制过去
                BeanUtils.copyProperties(dish,dto); // 将dish中的东西拷贝到dto中
//                且给dto设置分类名称
                dto.setCategoryName(categoryMap.get(dish.getCategoryId()));
//                将dto放入dtoList中
                dtoList.add(dto);
            });
        }

        // 6 返回page封装
        return R.success(new Page<>(dtoList, pageInfo.getTotal()));
    }

    @Override
    @Transactional
    public R save(DishDto dishDto) {
        // 1 保存菜品
        // 1.1 给菜品设置排序、时间
        dishDto.setSort(0);
        dishDto.setCreateTime(LocalDateTime.now());
        dishDto.setUpdateTime(dishDto.getCreateTime());
        // 1.2 调用dishmapper 完成保存
        // 注意 返回新增记录的主键!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        dishMapper.save(dishDto);

        // 2 保存菜品的口味   两种方案：保存一个口味插入一条  或者 发送一条批量保存语句insert into xxx values(),(),();
        // 2.1 先获取口味的集合 遍历获取每个口味对象
        List<DishFlavor> flavors = dishDto.getFlavors(); // 判断 因为可能没有口味
        if (CollUtil.isNotEmpty(flavors)) {
            flavors.forEach(f->{
                // 2.2 给口味对象设置 菜品id 修改时间更新时间 修改人 更新人
                f.setDishId(dishDto.getId());

                f.setCreateTime(dishDto.getCreateTime());
                f.setUpdateTime(dishDto.getUpdateTime());

                f.setCreateUser(dishDto.getCreateUser());
                f.setUpdateUser(dishDto.getUpdateUser());
            });
            // 2.3 保存所有口味对象
            dishFlavorMapper.batchSave(flavors);
        }
        // 3 注意这里需要添加spring事务 @Transactional 还需要在启动类上添加@EnableTransactionManagement
        // 因为菜品和菜品口味需要同时添加成功 要么同时失败

        // 清除分类下的菜品缓存数据
        // a.先查询所有的分类菜品key
        Set keys = redisTemplate.keys(DISH_ONSALE + "*");
        // b.清除这些key
        redisTemplate.delete(keys);
        return R.success(null);
    }


    @Override
    public R findById(Long id) {
        // 1 查询菜品基本信息
        Dish dish = dishMapper.findById(id);

        // 2 查询的口味信息
        List<DishFlavor> flavors = dishFlavorMapper.findByDishId(id);

        // 3 将两个信息封装成dto对象返回
        DishDto dto = new DishDto();
        BeanUtils.copyProperties(dish,dto);
        dto.setFlavors(flavors);

        return R.success(dto);
    }

    @Override
    @Transactional
    public R update(DishDto dishDto) {
        // 1 给我们dish设置修改时间 更新dish
        dishDto.setUpdateTime(LocalDateTime.now());
        dishMapper.update(dishDto);

        // 2 根据dishId删除口味表中的所有数据
        dishFlavorMapper.deleteByDishId(dishDto.getId());

        // 3 获取页面上传递过来的口味对象  批量保存
        List<DishFlavor> flavors = dishDto.getFlavors();
//        ctrl alt + m 抽取方法
        if (CollUtil.isNotEmpty(flavors)) {
            flavors.forEach(f->{
                // 2.2 给口味对象设置 菜品id 修改时间更新时间 修改人 更新人
                f.setDishId(dishDto.getId());

                f.setCreateTime(dishDto.getCreateTime());
                f.setUpdateTime(dishDto.getUpdateTime());

                f.setCreateUser(dishDto.getCreateUser());
                f.setUpdateUser(dishDto.getUpdateUser());
            });
            // 2.3 保存所有口味对象
            dishFlavorMapper.batchSave(flavors);
        }

        // a.先查询所有的分类菜品key
        Set keys = redisTemplate.keys(DISH_ONSALE + "*");
        // b.清除这些key
        redisTemplate.delete(keys);

        return R.success(null);
    }

    /**
     * 通过分类id查找在售的菜品集合
     * 逻辑：先看redis中有没有该分类下的菜品集合 若有直接返回，若无在查询mysql将查询中的数据放入redis中
     * @param categoryId 分类id
     * @return
     */
    @Override
    public R findByCategoryId(Long categoryId) {
        // 1 先看redis中有没有数据 有直接返回
        List<DishDto> dtoList = (List<DishDto>) redisTemplate.opsForValue().get(DISH_ONSALE+categoryId);
        if (CollUtil.isNotEmpty(dtoList)) {
            log.info("从redis中获取分类菜品信息" + dtoList);
            return R.success(dtoList);
        }

        List<Dish> list = dishMapper.findOnSaleByCategoryId(categoryId);
        // 为了让客户端页面上可以选择口味 我们需要将每个菜品的口味一并查出来


        // stream 流将list进行遍历加工 返回一个新集合  整理成1.8jdk新api
         dtoList = list.stream().map(d -> {
            List<DishFlavor> flavors = dishFlavorMapper.findByDishId(d.getId());
            DishDto dishDto = new DishDto();
            BeanUtils.copyProperties(d, dishDto);
            dishDto.setFlavors(flavors);
            return dishDto;
        }).collect(Collectors.toList());

        // 2 若没有查询mysql 将查询的数据存入redis
        redisTemplate.opsForValue().set(DISH_ONSALE+categoryId,dtoList);

        /*// a 先创建一个dtoList
        List<DishDto> dtoList = new ArrayList<>();
        // b 遍历dish集合 获取每个dish的id 查询flavor集合  将菜品和口味对象封装成dto对象 将dto加入dtoList中
        if (CollUtil.isNotEmpty(list)) {
            list.forEach(d->{
                List<DishFlavor> flavors = dishFlavorMapper.findByDishId(d.getId());
                DishDto dishDto = new DishDto();
                BeanUtils.copyProperties(d,dishDto);
                dishDto.setFlavors(flavors);

                dtoList.add(dishDto);

            });

            return R.success(dtoList);
        }*/

        return R.success(dtoList);
    }

/*    @Override
    @Transactional
    public R checkStatus0(List<Long> ids) {
        // 1 非空判断
        if (ArrayUtil.isEmpty(ids)) {
            throw new BusinessException("请选择要停售的菜品");
        }

        // 2 遍历菜品ids查询菜品状态 将已经停售的菜品id移除
        for (int i = 0; i < ids.size(); i++) {
            Dish dish = dishMapper.findStatusById(ids.get(i),0);
            if (dish != null){
                ids.remove(ids.get(i));
            }
        }

        System.out.println("============================");
        ids.forEach(System.out::println);
        System.out.println("============================");

        // 以上操作后就能够筛选出来的正在起售的菜品 ids 菜品禁用操作前需要先通过ids禁用菜品对应的启用套餐
        // 这就需要先通过ids遍历判断菜品是否有关联的正在起售的套餐，
        //      A 如果没有那么可以直接根据id修改dish状态
        //      B 如果有那么需要 1 通过id查找对应套餐setmeal   然后将套餐状态改为0（禁用）
        //                     2 并且把中间表setmeal_dish关联的数据删除
        //                     3 最后把dish菜品状态改为0

        for (Long id : ids) {
            // 通过菜品id查询套餐菜品关联关系表
            SetmealDish setmealDish = setmealDishMapper.findByDishId(id);
            if (setmealDish==null) {
                // 没有关联关系直接修改状态
                dishMapper.updateStatusById(id);
            }else {
                // 查询对应菜品正在起售的套餐集合
                List<Setmeal> setmealList = setmealMapper.findByDishIdAndStatus(setmealDish.getSetmealId(),1); //套餐id 起售
                // 修改套餐状态
                setmealMapper.updateStatusBatch(setmealList);
                // 把中间表setmeal_dish关联的数据删除
                setmealDishMapper.batchDeleteBySetmealIdsList(setmealList);
                // 把dish菜品状态修改为0
                dishMapper.updateStatusById(id);
            }
        }

        return R.success(null);
    }*/
}
